package org.hikari.qqbot.xiuxian.service.impl;

import cn.hutool.core.collection.CollUtil;
import cn.hutool.core.date.DateUnit;
import cn.hutool.core.date.DateUtil;
import cn.hutool.core.util.StrUtil;
import lombok.extern.slf4j.Slf4j;
import org.hikari.qqbot.client.http.HttpMessageClient;
import org.hikari.qqbot.client.http.HttpMessageClientConfig;
import org.hikari.qqbot.client.message.Message;
import org.hikari.qqbot.client.message.MessageSegment;
import org.hikari.qqbot.client.message.MessageType;
import org.hikari.qqbot.client.message.segment.AtMessageSegment;
import org.hikari.qqbot.client.message.segment.MarkdownMessageSegment;
import org.hikari.qqbot.xiuxian.command.CommandExecutionInfo;
import org.hikari.qqbot.xiuxian.command.CommandRunner;
import org.hikari.qqbot.xiuxian.command.ExecutableCommand;
import org.hikari.qqbot.xiuxian.command.ICommand;
import org.hikari.qqbot.xiuxian.service.ISystemSettingService;
import org.hikari.qqbot.xiuxian.service.ITaskService;
import org.hikari.qqbot.xiuxian.service.IUserService;
import org.hikari.qqbot.xiuxian.service.bo.UserSetting;
import org.hikari.qqbot.xiuxian.task.ITask;
import org.hikari.qqbot.xiuxian.task.TimedTaskHealthChecker;
import org.hikari.qqbot.xiuxian.task.UserTaskRuntime;
import org.hikari.qqbot.xiuxian.util.MessageUtil;
import org.springframework.beans.factory.ObjectProvider;
import org.springframework.boot.context.event.ApplicationReadyEvent;
import org.springframework.context.event.EventListener;
import org.springframework.stereotype.Service;

import java.util.*;
import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.ThreadLocalRandom;
import java.util.function.Function;
import java.util.stream.Collectors;

/**
 * created at 2024-12-10 22:29:42
 *
 * @author yinchao
 * @version 1.0
 **/
@Slf4j
@Service
public class DefaultTaskService implements ITaskService {

    private final Map<Long, UserTaskRuntime> userRuntimeMap = new ConcurrentHashMap<>();

    private final Map<String, ITask> taskMap;
    private final IUserService userService;
    private final ISystemSettingService systemSettingService;
    private final TimedTaskHealthChecker healthChecker;

    public DefaultTaskService(ObjectProvider<ITask> taskProvider,
                              IUserService userService,
                              ISystemSettingService systemSettingService) {
        this.taskMap = taskProvider.stream().collect(Collectors.toMap(ITask::getId, Function.identity()));
        this.userService = userService;
        this.systemSettingService = systemSettingService;
        this.healthChecker = new TimedTaskHealthChecker(systemSettingService.getSystemSetting());
    }

    @EventListener(ApplicationReadyEvent.class)
    public void initUserTasks() {
        Collection<? extends UserSetting> settings = userService.allUserSettings();
        if (CollUtil.isEmpty(settings)) {
            log.warn("[initUserTasks] 忽略初始化, 没有找到任何用户配置！");
            return;
        }

        if (CollUtil.isEmpty(taskMap)) {
            log.warn("[initUserTasks] 忽略初始化, 没有在容器中找到任何任务！");
            return;
        }

        for (UserSetting setting : settings) {

            if (CollUtil.isEmpty(setting.getActiveTasks())) {
                log.warn("[initUserTasks] 用户 '{}' 未配置任务", setting.getUserId());
                continue;
            }

            UserTaskRuntime runtime = getOrCreateUserTaskRuntime(setting);

            this.healthChecker.addRuntimeToCheckList(runtime);

            Collection<ITask> userTasks = runtime.getTasks();

            int i = 0;
            for (ITask task : runtime.getTasks()) {
                ICommand command = task.initCommand(setting);
                if (command != null) {
                    // 初始化延时，避免同时发送
                    command = command.delay(1000L * i++);
                    ExecutableCommand cmd = new ExecutableCommand(
                            task.getId(),
                            command.getCommand(),
                            runtime.getUserSetting().getUserId(),
                            runtime.getSystemSetting().getXiuXianBotId(),
                            null, // 初始化的适合发送到私聊
                            command.getSendTime(),
                            null
                    );
                    runtime.sendTaskCommand(cmd);
                }
            }

            log.info("[initUserTasks] 用户 '{}' 初始化({}/{})任务: {}", setting.getUserId(), i, userTasks.size(), userTasks.stream().map(ITask::getId).toList());
        }

        // 开启健康检查
        this.healthChecker.start();
    }

    @Override
    public void processMessage(Message message) {
        Long selfId = message.getSelf_id();
        UserSetting setting = userService.getUserSetting(selfId);
        if (setting == null) {
            log.warn("找不到登录用户({})配置! 忽略来自用户({}) 在{} 发送的消息: {}", selfId, message.getUser_id(), message.getGroup_id() == null ? "私聊" : "QQ群(" + message.getGroup_id() + ")", message.getRaw_message());
            return;
        }

        UserTaskRuntime runtime = getOrCreateUserTaskRuntime(setting);

        String messageContent = resolveMessage(runtime, message);

        if (messageContent == null) {
            return;
        }

        // 特殊消息处理
        if (messageContent.contains("你有此操作正在进行中，请稍后重试！")) {
            CommandExecutionInfo commandInfo = runtime.getMeta().getLastCommandInfo();
            if (commandInfo == null) {
                log.debug("接收到处理修仙之路返回的异常消息: {} 本地没有记录上一次命令, 忽略本条消息", messageContent);
            } else {
                Date lastTime = commandInfo.getActualSendTime();
                if (DateUtil.between(lastTime, new Date(), DateUnit.SECOND) > 10) {
                    log.info("接收到处理修仙之路返回的异常消息: {} 上一次命令执行时间: {} 距离当前时间超过10s, 可能是人为操作的消息, 忽略执行", messageContent, MessageUtil.getDisplaySendTime(lastTime));
                } else {
                    ExecutableCommand command = commandInfo.toExecutableCommand();
                    runtime.sendTaskCommand(command);
                    log.info("接收到处理修仙之路返回的异常消息: {} 重试上一次({})命令: {}", messageContent, commandInfo.getTaskId(), command);
                }
            }
            return;
        }

        // 每个用户串行执行
        synchronized (setting) {
            boolean processed = false;
            for (ITask task : runtime.getTasks()) {

                Collection<ICommand> commands;
                try {
                    commands = task.process(runtime, messageContent);
                } catch (Exception e) {
                    log.error("[{} - task({})] 处理消息存在异常. 跳过当前任务 msg = {}", selfId, task.getId(), message.getRaw_message(), e);
                    continue;
                }

                if (commands == null || commands.isEmpty()) {
                    continue;
                }

                StringBuilder commandStr = new StringBuilder();

                int i = 0;
                long randomDelay = ThreadLocalRandom.current().nextLong(setting.getRandomDelayMinSeconds() * 1000L, setting.getRandomDelayMaxSeconds() * 1000L);
                for (ICommand command : commands) {
                    if (command == null) {
                        continue;
                    }
                    ICommand delayCommand = delayCommand(command, randomDelay + i * 1000L);
                    runtime.sendTaskCommand(task.getId(), delayCommand);
                    commandStr.append(++i).append(".  ").append(delayCommand.getCommand()).append("\n");
                    processed = true;
                }

                log.info("[{} - task({})] \n[收到的消息]:\n{} \n\n[响应的命令]:\n{}\n", selfId, task.getId(), message.getRaw_message(), commandStr);
            }

            if (!processed) {
                log.info("[{}] 当前配置的任务[{}]无法处理本条消息, 消息内容: {}", selfId, runtime.getTasks().stream().map(ITask::getId).collect(Collectors.joining(", ")), message.getRaw_message());
            }
        }
    }

    public String resolveMessage(UserTaskRuntime runtime, Message message) {
        UserSetting setting = runtime.getUserSetting();
        // 开启群聊消息
        if (setting.isAllowGroupChat() && MessageType.GROUP.name().equalsIgnoreCase(message.getMessage_type())) {
            // 如果没有开启允许所有群组, 不在指定的聊天群，不进行处理
            if (!setting.isAllowAllChatGroup() &&
                    (setting.getAllowChatGroupIds() == null || !setting.getAllowChatGroupIds().contains(message.getGroup_id()))) {
                return null;
            }
            // 如果没有开启允许所有群聊消息, 过滤关于自己的消息
            if (!setting.isAllowAllGroupChatMessage() && !isAboutMe(message)) {
                return null;
            }
            return message.getRaw_message();
        }
        // 开启私聊消息
        if (setting.isAllowPrivateChat() && MessageType.PRIVATE.name().equalsIgnoreCase(message.getMessage_type())) {
            // 如果没有开启允许所有消息, 只处理发送方为修仙之路的消息
            if (!setting.isAllowAllPrivateChatUser() && !Objects.equals(message.getUser_id(), runtime.getSystemSetting().getXiuXianBotId())) {
                return null;
            }
            return message.getRaw_message();
        }
        return null;
    }

    private boolean isAboutMe(Message message) {
        List<MessageSegment> segments = message.getMessage();
        for (MessageSegment segment : segments) {
            if (AtMessageSegment.TYPE.equalsIgnoreCase(segment.getType())) {
                AtMessageSegment at = new AtMessageSegment();
                at.fromSegment(segment);
                if (Objects.equals(message.getSelf_id(), at.getQq())) {
                    return true;
                }
            }
            if (MarkdownMessageSegment.TYPE.equalsIgnoreCase(segment.getType())) {
                MarkdownMessageSegment seg = new MarkdownMessageSegment();
                seg.fromSegment(segment);
                if (StrUtil.isNotBlank(seg.getContent()) && seg.getContent().contains("at_tinyid=" + message.getSelf_id())) {
                    return true;
                }
            }
        }
        return false;
    }

    private ICommand delayCommand(ICommand command, long delay) {
        if (delay <= 0) {
            return command;
        }
        return command.delay(delay);
    }

    private List<ITask> getUserTasks(UserSetting setting) {
        return setting.getActiveTasks()
                .keySet()
                .stream()
                .map(taskMap::get)
                .filter(Objects::nonNull)
                .toList();
    }

    public UserTaskRuntime getUserTaskRuntime(Long userId) {
        return userRuntimeMap.get(userId);
    }

    private UserTaskRuntime getOrCreateUserTaskRuntime(UserSetting setting) {
        return userRuntimeMap.computeIfAbsent(setting.getUserId(), uid -> {
            List<ITask> userTasks = getUserTasks(setting);
            userTasks.forEach(task -> task.initSetting(setting));
            CommandRunner runner = createCommandRunner(setting);
            runner.start();
            return new UserTaskRuntime(systemSettingService.getSystemSetting(), setting, runner, userTasks);
        });
    }

    private CommandRunner createCommandRunner(UserSetting setting) {
        HttpMessageClientConfig config = new HttpMessageClientConfig();
        config.setUrl(setting.getServerUrl());
        HttpMessageClient client = new HttpMessageClient(config);
        return new CommandRunner(String.valueOf(setting.getUserId()), client);
    }

}
